/**
 * forum zhanjung
 */
package com.junhong.forum.backingbean;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.enterprise.event.Event;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import com.junhong.auth.common.Login;
import com.junhong.auth.entity.User;
import com.junhong.forum.common.Constants;
import com.junhong.forum.common.Messages;
import com.junhong.forum.datamodel.LazyThreadDataModel;
import com.junhong.forum.entity.AbstractEntity;
import com.junhong.forum.entity.ForumCategory;
import com.junhong.forum.entity.ForumThread;
import com.junhong.forum.exceptions.AuthorizationFailException;
import com.junhong.forum.service.CategoryServiceSingleton;
import com.junhong.forum.service.ThreadEjb;
import com.junhong.forum.stats.CacheKey;
import com.junhong.forum.stats.CacheType;
import com.junhong.forum.stats.HitCache;
import com.junhong.forum.stats.RatingCache;
import com.junhong.util.ViewScoped;

/**
 * @author zhanjung
 * 
 */

@Named
@ViewScoped
public class ThreadBackingBean extends AbstractBacking {
	private static final long		serialVersionUID		= 01l;

	/* ------------instance Variable-------------- */

	// this will determine if it is in view/edit mode
	private boolean					editMode				= false;

	@Inject
	private LazyThreadDataModel		lazyThreadDataModel;

	@Inject
	ForumThread						selectedForumThread;
	@Inject
	private Login					login;
	@EJB
	ThreadEjb						threadEjb;
	@EJB
	CategoryServiceSingleton		categoryEjb;
	@EJB
	private HitCache				hitCache;

	@EJB
	private RatingCache				ratingCache;

	private ForumCategory			belongingCategory;

	// user's current position event
	@Inject
	private Event<ForumCategory>	currentCategoryEvent;
	@Inject
	private Event<ForumThread>		currentForumThreadEvent;
	private User					currUser;
	private boolean					ownerForCurrCategory	= false;

	@PostConstruct
	public void initialize() {
		int id = -1;
		Object cat_thread_id = this.getSessionMap().get(Constants.CATEGORY__THREAD_ID);
		if (cat_thread_id != null) {
			id = Integer.parseInt((String) cat_thread_id);
			Object belongCategory = this.getSessionMap().get(Constants.BelongingCategory);
			if (null != belongCategory && ((ForumCategory) belongCategory).getId() == id) {
				belongingCategory = (ForumCategory) belongCategory;
			} else {
				belongingCategory = categoryEjb.getForumCategoryById(id);
				this.getSessionMap().put(Constants.BelongingCategory, belongingCategory);
			}
		} else {
			belongingCategory = (ForumCategory) this.getSessionMap().get(Constants.BelongingCategory);

		}

		currUser = getCurrentUser();

		// populate isOwnerForCurrCategory
		ownerForCurrCategory = calculateOwnerForCurrCategory(currUser, belongingCategory);

	}

	public boolean calculateOwnerForCurrCategory(User user, ForumCategory category) {

		boolean result = false;
		if (user != null && category != null && user.equals(category.getOwner())) {
			result = true;
		}
		return result;

	}

	/* -------------business logic----------------- */
	/**
	 * @param forumThread
	 * @return
	 */
	public String createThread() {

		if (currUser == null) {
			currUser = this.getCurrentUser();
		}
		this.selectedForumThread.setContent("<p>" + this.selectedForumThread.getContent() + "</p>");
		this.selectedForumThread.setCategory(belongingCategory);
		this.selectedForumThread.setOwner(currUser);
		processBusinessWithAuthorizationCheck(Constants.Action_CREATE, this.selectedForumThread);

		return Constants.NavSuccess;

	}

	/**
	 * prerender view
	 */
	public void calculateNavMap() {
		if (belongingCategory != null) {
			// update the navigation map
			currentCategoryEvent.fire(belongingCategory);
		}
	}

	/**
	 * pre renderview for viewing thread
	 */
	public void loadThread() {

		int threadId = 0;
		try {
			Object threadIdObj = this.getSessionMap().get("threadId");
			threadId = Integer.parseInt((String) threadIdObj);
		} catch (NumberFormatException e) {
			setBizErrorNSkipToResp("INVALIDTHREADID");
			selectedForumThread = null;
			return;
		}
		selectedForumThread = threadEjb.getForumThreadById(threadId);
		if (selectedForumThread == null) {
			setBizErrorNSkipToResp("INVALIDTHREADID");
			selectedForumThread = null;
			return;

		} else {
			belongingCategory = selectedForumThread.getCategory();
			hitCache.incrementAndGet(new CacheKey(selectedForumThread.getId(), CacheType.THREAD));
			currentForumThreadEvent.fire(selectedForumThread);
		}

	}

	public ForumThread refreshThread(ForumThread forumThread) {
		return threadEjb.getForumThreadById(forumThread.getId());
	}

	public String updateThread() {

		processBusinessWithAuthorizationCheck(Constants.Action_UPDATE, selectedForumThread);
		selectedForumThread = refreshThread(selectedForumThread);
		this.enableEditMode(false);
		return Constants.NavNull;
	}

	@Override
	protected void processBusiness(String action, AbstractEntity entity) throws AuthorizationFailException {
		// TODO Auto-generated method stub
		if (currUser == null) {
			currUser = this.getCurrentUser();
		}
		ForumThread thread = (ForumThread) entity;

		if (action.equals(Constants.Action_UPDATE)) {
			threadEjb.updateForumThread(thread);

		}
		if (action.equals(Constants.Action_CREATE)) {
			threadEjb.createForumThread(thread);

			categoryEjb.updateNumOfThread(belongingCategory, +1);
			userEjb.updateNumOfPosts(currUser, +1);
		}
		if (action.equals(Constants.Action_DELETE)) {
			threadEjb.unDashBoardThread(thread);
			threadEjb.deleteForumThread(thread);

			categoryEjb.updateNumOfThread(belongingCategory, -1);
			userEjb.updateNumOfPosts(currUser, -1);
		}
		if (action.equals(Constants.Action_DASHBOARD)) {

			threadEjb.dashBoardThread(thread);
		}
		if (action.equals(Constants.Action_UNDASHBOARD)) {

			threadEjb.unDashBoardThread(thread);
		}

	}

	/**
	 * @param thread
	 * @return
	 */
	public void deleteThread(ForumThread thread) {

		processBusinessWithAuthorizationCheck(Constants.Action_DELETE, thread);
	}

	public void topThread(ForumThread thread, boolean top) {

		thread.setTopped(top);
		thread.setToppedTime(new Date());
		processBusinessWithAuthorizationCheck(Constants.Action_UPDATE, thread);
	}

	/**
	 * get the loaded thread
	 * 
	 * @return
	 */
	public ForumThread getLoadedThread() {
		ForumThread loadedThread = threadEjb.getLoadedThreadById(selectedForumThread.getId());
		return loadedThread;
	}

	/**
	 * 
	 * @param rating
	 *            values [1,-1,0]
	 *            Score: number of votes
	 */
	public void handleVotes(int vote) {
		int newVote = ratingCache.incrementAndGet(new CacheKey(selectedForumThread.getId(), CacheType.THREAD));
		this.getRequestMap().put("score", newVote);
	}

	public AtomicInteger getVote(int id) {
		return ratingCache.getVote(new CacheKey(id, CacheType.THREAD));
	}

	/**
	 * concurrent access is to be considered later. should be fine since it
	 * cause insert of the recordd on the db
	 */
	public void stickThread() {
		User user = login.getCurrentUser();
		user = userEjb.getUserWithEagerStickyThread(user.getId());

		user.getStickyThreads().add(this.selectedForumThread);
		// selectedForumThread =
		// threadEjb.getThreadWithLoadedStickiedByUserById(this.selectedForumThread.getId());
		// this.selectedForumThread.getStickiedByUsers().add(user);
		// threadEjb.updateForumThreadWithAuthorization(this.selectedForumThread);
		userEjb.updateUser(user);

	}

	/**
	 * concurrent access is to be considered later.
	 */
	public void unStickThread() {
		User user = login.getCurrentUser();
		user = userEjb.getUserWithEagerStickyThread(user.getId());
		user.getStickyThreads().remove(this.selectedForumThread);
		userEjb.updateUser(user);

	}

	public void lockThread(boolean status) {
		this.selectedForumThread = threadEjb.getForumThreadById(this.selectedForumThread.getId());
		this.selectedForumThread.setLocked(status);
		processBusinessWithAuthorizationCheck(Constants.Action_UPDATE, selectedForumThread);
	}

	public void enableEditMode(boolean editMode) {
		if (editMode) {
			selectedForumThread = this.refreshThread(selectedForumThread);
		}
		this.setEditMode(editMode);
	}

	/**
	 * put thread on the main dashboard
	 * 
	 * @param thread
	 */
	public void dashBoardThread(ForumThread thread) {
		processBusinessWithAuthorizationCheck(Constants.Action_DASHBOARD, thread);
		FacesMessage message = Messages.getMessage("", "SUCCEED", null, false);
		message.setSeverity(FacesMessage.SEVERITY_INFO);
		FacesContext.getCurrentInstance().addMessage(null, message);
	}

	public void unDashBoardThread(ForumThread thread) {
		processBusinessWithAuthorizationCheck(Constants.Action_UNDASHBOARD, thread);
		FacesMessage message = Messages.getMessage("", "SUCCEED", null, false);
		message.setSeverity(FacesMessage.SEVERITY_INFO);
		FacesContext.getCurrentInstance().addMessage(null, message);
	}

	/* -------------getter/setter----------------- */

	public ForumThread getSelectedForumThread() {
		return selectedForumThread;
	}

	public void setSelectedForumThread(ForumThread selectedForumThread) {
		this.selectedForumThread = selectedForumThread;
	}

	public ForumCategory getBelongingCategory() {
		return belongingCategory;
	}

	public void setBelongingCategory(ForumCategory belongingCategory) {
		this.belongingCategory = belongingCategory;
	}

	public boolean isEditMode() {
		return editMode;
	}

	public void setEditMode(boolean editMode) {
		this.editMode = editMode;
	}

	public LazyThreadDataModel getLazyThreadDataModel() {
		return lazyThreadDataModel;
	}

	public void setLazyThreadDataModel(LazyThreadDataModel lazyThreadDataModel) {
		this.lazyThreadDataModel = lazyThreadDataModel;
	}

	/**
	 * @return the serialversionuid
	 */
	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	/**
	 * @return the ownerForCurrCategory
	 */
	public boolean isOwnerForCurrCategory() {
		return ownerForCurrCategory;
	}

	/**
	 * @param ownerForCurrCategory
	 *            the ownerForCurrCategory to set
	 */
	public void setOwnerForCurrCategory(boolean ownerForCurrCategory) {
		this.ownerForCurrCategory = ownerForCurrCategory;
	}

}
